﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Web;

namespace HttpLib
{
    public class HttpClient
    {
        #region 工具方法
        /// <summary>
        /// POST提交时的标准ContentType
        /// </summary>
        public const string PostFormContentType = "application/x-www-form-urlencoded";
        /// <summary>
        /// 使用指定的编码对象对URL字符串进行编码
        /// </summary>
        /// <param name="str">待编码字符</param>
        /// <param name="e">字符编码</param>
        /// <returns>已编码字符</returns>
        public static string UrlEncode(string str, Encoding e)
        {
            return HttpUtility.UrlEncode(str, e);
        }

        /// <summary>
        /// 使用指定的编码对象将URL编码字符串进行解码
        /// </summary>
        /// <param name="str">待解码字符</param>
        /// <param name="e">字符编码</param>
        /// <returns>已解码字符</returns>
        public static string UrlDecode(string str, Encoding e)
        {
            return HttpUtility.UrlDecode(str, e);
        }

        /// <summary>
        /// 用Header中的cookie字符串构建CookieContainer
        /// </summary>
        /// <param name="url"></param>
        /// <param name="cookieHeader"></param>
        /// <returns></returns>
        public static CookieContainer BuildCookieContainer(string url, string cookieHeader)
        {
            var cc = new CookieContainer();
            cc.SetCookies(new Uri(url), cookieHeader);
            return cc;
        }
        #endregion

        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="encoding">字符编码</param>
        /// <param name="headers">协议标头</param>
        /// <param name="cookie">cookie集合容器</param>
        /// <param name="timeout">超时时间（默认3秒）</param>
        /// <returns>服务端返回结果</returns>
        public static RemoteRsponseContent Get(string url, Encoding encoding, WebHeaderCollection headers, CookieContainer cookie, int timeout = 3000)
        {
            try
            {
                var request = (HttpWebRequest) WebRequest.Create(url);

                request.ReadWriteTimeout = timeout;
                request.Timeout = timeout;
                request.Method = "GET";

                CopyHeadersTo(request, headers);

                if (cookie != null)
                {
                    request.CookieContainer = cookie;
                }

                return new RemoteRsponseContent {StatusCode = 200, Content = GetResponse(request, encoding)};
            }
            catch (WebException w)
            {
                return RemoteWebException(w);
            }
            catch (Exception e)
            {
                return new RemoteRsponseContent {StatusCode = 500, Content = string.Empty, ErrorMessage = e.Message};
            }
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url">地址</param>
        /// <param name="postData">post内容</param>
        /// <param name="encoding">字符编码</param>
        /// <param name="headers">协议标头</param>
        /// <param name="cookie">cookie集合容器</param>
        /// <param name="timeout">超时时间（默认3秒）</param>
        /// <returns>服务端返回结果</returns>
        public static RemoteRsponseContent Post(string url, NameValueCollection postData, Encoding encoding, WebHeaderCollection headers, CookieContainer cookie, int timeout = 3000)
        {
            try
            {
                var request = (HttpWebRequest)WebRequest.Create(url);

                request.ReadWriteTimeout = timeout;
                request.Timeout = timeout;
                request.KeepAlive = true;
                request.Method = "POST";

                if (headers != null && headers.Count > 0)
                {
                    var contentType = headers[HttpRequestHeader.ContentType];
                    if (contentType != null &&
                        string.Compare(contentType, PostFormContentType, StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        headers[HttpRequestHeader.ContentType] = PostFormContentType;
                    }
                    CopyHeadersTo(request, headers);
                }

                if (cookie != null)
                {
                    request.CookieContainer = cookie;
                }

                var postBytes = BuildPostBody(postData, encoding);
                using (var content = new MemoryStream(postBytes))
                {
                    request.ContentLength = content.Length;
                    using (var postStream = request.GetRequestStream())
                    {
                        CopyTo(content, postStream);
                    }
                }
                return new RemoteRsponseContent { StatusCode = 200, Content = GetResponse(request, encoding) };
            }
            catch (WebException w)
            {
                return RemoteWebException(w);
            }
            catch (Exception e)
            {
                return new RemoteRsponseContent { StatusCode = 500, Content = string.Empty, ErrorMessage = e.Message };
            }
        }

        #region 私有方法
        private static string GetResponse(HttpWebRequest request, Encoding encoding)
        {
            using (var response = request.GetResponse() as HttpWebResponse)
            {
                if (response != null)
                    using (var responseStream = response.GetResponseStream())
                    using (var reader = new StreamReader(responseStream, TryGetEncoding(response, encoding)))
                    {
                        return reader.ReadToEnd();
                    }
            }
            return string.Empty;
        }

        private static RemoteRsponseContent RemoteWebException(WebException we)
        {
            if (we.Response == null) return RemoteRsponseContent.Empty;
            try
            {
                using (we.Response)
                {
                    var r = (HttpWebResponse)we.Response;
                    using (var s = we.Response.GetResponseStream())
                    {
                        using (var sr = new StreamReader(s))
                        {
                            return new RemoteRsponseContent
                            {
                                StatusCode = (int)r.StatusCode,
                                Content = string.Empty,
                                ErrorMessage = sr.ReadToEnd()
                            };
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                return new RemoteRsponseContent { StatusCode = 500, Content = string.Empty, ErrorMessage = ex.Message };
            }
        }

        private static void CopyTo(Stream input, Stream output)
        {
            var buffer = new byte[0x1000];
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, read);
            }
        }

        private static Encoding TryGetEncoding(HttpWebResponse response, Encoding defaultEncoding )
        {
            try
            {
                return Encoding.GetEncoding(response.CharacterSet);
            }
            catch
            {
                return defaultEncoding;
            }
        }

        private static void CopyHeadersTo(HttpWebRequest request, WebHeaderCollection headers)
        {
            if (headers == null) return;
            if (headers.Count==0) return;

            var text = headers["Accept"];
            var text2 = headers["Connection"];
            var text3 = headers["Content-Type"];
            var text4 = headers["Expect"];
            var text5 = headers["Referer"];
            var text6 = headers["User-Agent"];

            headers.Remove("Accept");
            headers.Remove("Connection");
            headers.Remove("Content-Type");
            headers.Remove("Expect");
            headers.Remove("Referer");
            headers.Remove("User-Agent");

            request.Headers = headers;

            if (!string.IsNullOrEmpty(text))
            {
                request.Accept = text;
            }
            if (!string.IsNullOrEmpty(text2))
            {
                request.Connection = text2;
            }
            if (!string.IsNullOrEmpty(text3))
            {
                request.ContentType = text3;
            }
            if (!string.IsNullOrEmpty(text4))
            {
                request.Expect = text4;
            }
            if (!string.IsNullOrEmpty(text5))
            {
                request.Referer = text5;
            }
            if (!string.IsNullOrEmpty(text6))
            {
                request.UserAgent = text6;
            }
        }

        private static byte[] BuildPostBody(NameValueCollection data, Encoding encoding)
        {
            var value = string.Empty;
            var sder = new StringBuilder();
            foreach (var key in data.AllKeys)
            {
                sder.Append(value);
                // 外面处理，不统一编码
                //sder.Append(UrlEncode(key, Encoding.UTF8));
                //sder.Append(UrlEncode(key, encoding));
                sder.Append(key);
                sder.Append("=");
                //sder.Append(UrlEncode(data[key], Encoding.UTF8));
                //sder.Append(UrlEncode(data[key], encoding));
                sder.Append(data[key]);
                value = "&";
            }
            return encoding.GetBytes(sder.ToString());
        }
        #endregion

    }
}
